/****************************************************************

kernel/kb.c
Keyboard driver for AWOS

Copyright (c) 2006 Andrew Wilcox. All rights reserved.

Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.

3. The end-user documentation included with the redistribution,
if any, must include the following acknowledgment:

"This product includes software developed by Andrew Wilcox for
AWOS. (http://code.google.com/p/awos)."

Alternately, this acknowledgment may appear in the software
itself, if and wherever such third-party acknowledgments normally
appear.

4. The names "AWOS" and "Andrew Wilcox" must not be used to
endorse or promote products derived from this software without
prior written permission. For written permission, please contact
Andrew.R.Wilcox@Gmail.com.

5. Products derived from this software may not be called "AWOS"
 nor may "AWOS" appear in their name, without prior written
 permission of Andrew Wilcox.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDREW WILCOX BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

****************************************************************/

#include <kernel.h>
#include <conio.h>
#include <driver/kb.h>
#include <acpi.h>
#include <asm.h>

static unsigned short KiKbdStatus;
void KiIrq1(struct regs *);
unsigned KiKbdConvert(unsigned);
void KiKbdHandleExKey(unsigned);
void AcpiPowerEvent(int);
extern BOOL debug;

void KsKeyb()
{
	KiIrqInstall(1, KiIrq1);
	return;
}

void KiIrq1(struct regs *r)
{
	unsigned key, i;
	
	if(r->int_no != 0x01) return;
	
	key = inportb(0x60);
	
	switch(key)
	{
		//handle all the special keys here
		default:
			i = KiKbdConvert(key);
			if(i != 0) putch(i);
			break;
	}
	
	return;
}

unsigned KiKbdConvert(unsigned key)
{
	//ugh...ugly ugly code, but oh well
	BOOL bBreak = FALSE;
	static BOOL bExKey;
	unsigned short temp;
	static unsigned char set1_map[] = {
	0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t',
	'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's',
	'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
	'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' ', 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
	KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, '-', KEY_LFT, '5', KEY_RT, '+', KEY_END,
	KEY_DN, KEY_PGDN, KEY_INS, KEY_DEL, 0, 0, 0, KEY_F11, KEY_F12
	};
	static unsigned char set1_map_shift[] = {
	0, 0x1B, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t',
	'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S',
	'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V',
	'B', 'N', 'M', '<', '>', '?', 0, 0, 0, ' ', 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
	KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, '-', KEY_LFT, '5', KEY_RT, '+', KEY_END,
	KEY_DN, KEY_PGDN, KEY_INS, KEY_DEL, 0, 0, 0, KEY_F11, KEY_F12
	};
	
	if(key == 0xE0)
	{
		bExKey = TRUE;
		return 0;
	}
	
	if(key >= 0x80)
	{
		bBreak = TRUE;
		key &= 0x7F;
	}
	
	//UGH - ' && !bExKey' had to be added because some weird, funky keyboards
	//send invalid key numbers after E0
	//e.g. Microsoft Office keyboard sends E0 8B for 'scroll down 1x'
	if(bBreak && !bExKey)
	{
		switch(key)
		{
		case RAW_LSHIFT:
		case RAW_RSHIFT:
			KiKbdStatus &= ~KBD_SHIFT;
			break;
		case RAW_ALT:
			KiKbdStatus &= ~KBD_ALT;
			break;
		case RAW_CONTROL:
			KiKbdStatus &= ~KBD_CTRL;
			break;
		default:
			break;
		}
		goto ret0;
	}
	
	switch(key)
	{
	case 0x01:
		if(KiKbdStatus & KBD_CTRL)
			KiKbdHandleExKey(0x5B);
		else
			//do ESC processing...
		goto ret0;
	case RAW_ALT:
		KiKbdStatus |= KBD_ALT;
		goto ret0;
	case RAW_CONTROL:
		KiKbdStatus |= KBD_CTRL;
		goto ret0;
	case RAW_LSHIFT:
	case RAW_RSHIFT:
		KiKbdStatus |= KBD_SHIFT;
		bExKey = FALSE;
		goto ret0;
	case RAW_LOCK_SCR:
		KiKbdStatus ^= KBD_LOCKS;
		goto LED;
	case RAW_LOCK_NUM:
		KiKbdStatus ^= KBD_LOCKN;
		goto LED;
	case RAW_LOCK_CAP:
		KiKbdStatus ^= KBD_LOCKC;
LED:	outportb(0x60, 0xED);
		temp = 0;
		if(KiKbdStatus & KBD_LOCKS)
			temp |= 1;
		if(KiKbdStatus & KBD_LOCKN)
			temp |= 2;
		if(KiKbdStatus & KBD_LOCKC)
			temp |= 4;
		outportb(0x60, temp);
		goto ret0;
	default:
		break;
	}
	
	if(bExKey)
	{
		if(bBreak) key += 0x80;
		KiKbdHandleExKey(key);
		bExKey = FALSE;
		return 0;
	}
	
	if(key >= sizeof(set1_map) / sizeof(set1_map[0]))
	{
		dprintf("kb: key >= sizeof(map)");
		return 0;
	}
	
	temp = set1_map[key];
	if(KiKbdStatus & KBD_SHIFT)
		temp = set1_map_shift[key];
	
	if(debug) printf("%u\n", temp);
	
	if(temp == 0x00 || temp == 0xFF)
	{
		printf("\nKeyboard controller error (scancode 0x%02X).  Please ensure the keyboard is\nfully inserted into the port, and restart your computer.  If the\nproblem persists, contact your keyboard vendor.", temp);
		return 0;
	}
	
	if((KiKbdStatus & KBD_CTRL) && (KiKbdStatus & KBD_ALT))
	{
		switch(temp)
		{
			case KEY_DEL:
				clrscr();
				printf("AWOS is restarting your computer.  Please wait...");
				asm("CLI");
				temp = 0;
				//******
				//is this block really necessary?
				do {
					temp = inportb(0x64);
					if((temp & 0x01) != 0)
					{
						(void)inportb(0x60);
						continue;
					}
				} while((temp & 0x02) != 0);
				//******
				outportb(0x64, 0xFE);
				asm("INT $0x20" :: "a"(3)); //FE it
				break;
			case KEY_F1:
			case KEY_F2:
			case KEY_F3:
			case KEY_F4:
			case KEY_F5:
			case KEY_F6:
			case KEY_F7:
			case KEY_F8:
				printf("Changing virtual consoles is not yet supported.\n");
				break;
			case KEY_F9:
				printf("Graphics mode is not yet supported.\n");
				break;
			case KEY_F10:
				//okay this is where the debug/log goes
				break;
			default:
				break;
		};
	}
	
	return temp;

ret0:
	bExKey = FALSE;
	return 0;
}

void KiKbdHandleExKey(unsigned code)
{
	switch(code)
	{
	case 0x05:
		//MS Office: files
		break;
	case 0x07:
		//MS: redo
		break;
	case 0x08:
		//MS: undo
		break;
	case 0x09:
		//MS: app left (?)
		break;
	case 0x0A:
		//MS: paste
		break;
	case 0x0B:
		//MS: scroll up 1x
		break;
	case 0x10:
		//Compaq/MS: previous track
		break;
	case 0x11:
		//MS: scroll up 2x
		break;
	case 0x12:
		//MS: scroll up 3x
		break;
	case 0x13:
		//MS: start Word
		//Compaq: online community (people)
		break;
	case 0x14:
		//Compaq: online 'Q' button
		//MS: start Excel
		break;
	case 0x15:
		//Compaq: online services (bulb)
		//MS: calendar
		break;
	case 0x16:
		//MS: logoff
		break;
	case 0x17:
		//MS: cut
		break;
	case 0x18:
		//MS: copy
	case 0x19:
		//Compaq: next track
		break;
	case 0x1A:
		//Compaq: the icon is a spyglass, search?
		break;
	case 0x1B:
		//Compaq: I can't tell what the icon is
		break;
	case 0x1E:
		//Compaq: email
		//MS: app right(?)
		break;
	case 0x1F:
		//Compaq: app launch
		//MS: scroll up 4x
		break;
	case 0x20:
		//Compaq/MS: mute
		break;
	case 0x21:
		//Compaq: search
		//MS: calculator
		break;
	case 0x22:
		//Compaq/MS: play or pause
		break;
	case 0x23:
		//Compaq: start internet 'i'
		//MS: spell check
		break;
	case 0x24:
		//Compaq: stop
		break;
	case 0x2C:
		//Compaq: eject
		break;
	case 0x2E:
		//Compaq/MS: volume down
		break;
	case 0x30:
		//Compaq/MS: play it louder!
		break;
	case 0x32:
		//Compaq: eCommerce (shopping cart)
		//MS: Web home
		break;
	case 0x3B:
		//MS: help
		break;
	case 0x3C:
		//MS: Office Home
		break;
	case 0x3D:
		//MS: task pane
		break;
	case 0x3E:
		//MS: new
		break;
	case 0x3F:
		//MS: open
		break;
	case 0x40:
		//MS: close
		break;
	case 0x41:
		//MS: reply
		break;
	case 0x42:
		//MS: forward
		break;
	case 0x43:
		//MS: send
		break;
	case 0x57:
		//MS: save
		break;
	case 0x58:
		//MS: print
		break;
	case 0x5B:
		//left Windows key
	case 0x5C:
		//right Windows key
		break;
	case 0x5D:
		//context menu ("Application") key
		break;
	case 0x5E:
		//power key
		AcpiPowerEvent(ACPI_KEY_POWER);
		break;
	case 0x5F:
		//sleep key
		AcpiPowerEvent(ACPI_KEY_SLEEP);
		break;
	case 0x63:
		//wake key
		AcpiPowerEvent(ACPI_KEY_WAKE);
		break;
	case 0x64:
		//MS: My Pics
		break;
	case 0x65:
		//MS: search
		break;
	case 0x66:
		//MS: favorites
		break;
	case 0x67:
		//MS: refresh
		break;
	case 0x68:
		//Compaq: quick print
		//MS: Inet stop
		break;
	case 0x69:
		//MS: forward
		break;
	case 0x6A:
		//MS: back
		break;
	case 0x6B:
		//MS: My Computer
		break;
	case 0x6C:
		//MS: mail
		break;
	case 0x6D:
		//MS: media
		break;
	case (RAW_ALT + 0x80):
		KiKbdStatus &= ~KBD_ALT;
		break;
	case (RAW_CONTROL + 0x80):
		KiKbdStatus &= ~KBD_CTRL;
		break;
	default:
		dprintf("kb: unknown extended scancode 0x%02X", code);
		break;
	}
	
	return;
}
